문단 클릭 = 시작 위치 선택 · 재생은 우하단 버튼/P (재생 중 클릭 = 점프)

7장. 리액트 에이전트를 활용한 RAG

출처 — 브라이스 유·조경아·박수진·김재웅, 『RAG 마스터: 랭체인으로 완성하는 LLM 서비스』(프리렉, 2025), 7장 (pp. 427~452). 원문 PDF rag_master_final_v11_260910.pdf (2026-09-10 판)

대규모 언어 모델에게 '생각의 사슬'로 먼저 풀이 과정을 쓰게 하고, 그 생각을 발판 삼아 여러 검색 도구 중 하나를 스스로 골라 실행하는 리액트(ReAct) 에이전트를 랭체인으로 직접 만든다.

학습 목표

이 장을 끝내면 다음을 할 수 있다. - ReAct 방법론의 '생각 → 행동 → 행동 입력 → 관찰' 사이클을 설명한다. - 생각의 사슬(CoT) 프롬프트가 왜 정답률을 높이는지 설명한다. - PDF 문서로부터 벡터 검색기를 만들고 이를 에이전트 도구로 변환한다. - 에이전트가 쓸 프롬프트를 직접 작성하고 {tools}·{tool_names}·{input}·{agent_scratchpad} 각 변수의 역할을 구분한다. - 여러 검색 도구를 가진 에이전트를 만들어 복합 질문에 단계적으로 답하게 한다.

전체 흐름도

사용자 질문
    │
    ▼
┌───────────────────────────────────────────────────┐
│                ReAct 에이전트 루프                  │
│                                                     │
│   Thought(생각) ──────▶ Action(행동 선택)           │
│        ▲                      │                    │
│        │                      ▼                    │
│        │              Action Input(행동 입력)       │
│        │                      │                    │
│        │                      ▼                    │
│        │                  도구 실행                 │
│        │                      │                    │
│        └────── Observation(관찰) ◀──────────────────┘
│                                                     │
│        (충분한 정보를 모을 때까지 반복)              │
└───────────────────────────────────────────────────┘
    │
    ▼
Thought: 이제 최종 답을 알았습니다
    │
    ▼
Final Answer(최종 답변)

에이전트가 실행되기 전, 도구 자체는 다음 순서로 준비된다.

japan ICT PDF ──┐                              ┌──▶ japan_ict (도구)
                 ├──▶ create_pdf_retriever ─────┤
usa ICT PDF   ──┘   (로드→청킹→임베딩→저장)      └──▶ usa_ict (도구)
                                                        │
                                                        ▼
                              tools = [japan_ict, usa_ict]
                                                        │
                                                        ▼
                        create_react_agent(llm, tools, prompt)
                                                        │
                                                        ▼
                          AgentExecutor(react_agent, tools)
                                                        │
                                                        ▼
                              react_agent_executor.invoke(질문)

0. 용어 사전

참고 — 위쪽 4개는 이 장을 읽기 전에 알아야 하는 선행 용어다. 낯설면 2장(검색 증강 생성 기초와 실습)·1장(랭체인 살펴보기)을 먼저 보라.

한글 용어 원문 영문명 의미
검색기 Retriever (선행) 사용자 질문과 관련성이 높은 문서를 벡터 데이터베이스에서 찾아 돌려주는 컴포넌트. 도서관에서 사서가 질문에 맞는 책을 골라 오는 역할에 비유할 수 있다. 본문 §2.2
임베딩 Embedding (선행) 문장·단어를 숫자 벡터로 바꿔 의미가 비슷한 것끼리 가깝게 배치하는 표현 방식. 본문 §2.1·§2.2
벡터 데이터베이스 Vector Database (선행) 임베딩 벡터를 저장하고 유사도 검색을 수행하는 저장소(이 장에서는 Chroma). 본문 §2.2
프롬프트 템플릿 PromptTemplate (선행) 변수 자리표시자를 넣어 재사용 가능하게 만든 프롬프트 형식. 본문 §2.3
에이전트 Agent 목표를 인식하고 해결 방법을 추론하며 다음 행동을 계획·실행할 수 있는 AI 시스템. 이 장의 핵심 주제
리액트 ReAct (Reason + Act) 추론(Reason)과 행동(Act)을 결합해 '생각 → 행동 → 행동 입력 → 관찰' 사이클을 목표 달성까지 반복하는 에이전트 방법론
생각의 사슬 Chain of Thought, CoT 답을 정하기 전에 풀이 과정을 먼저 글로 쓰게 해 정답률을 높이는 프롬프트 기법
생각 Thought 사이클의 한 단계 — 현재 상황을 분석하고 다음 행동을 추론한다
행동 Action 사이클의 한 단계 — 여러 도구 중 어느 것을 쓸지 선택한다
행동 입력 Action Input 선택한 행동에 넘길 구체적인 값을 정한다
관찰 Observation 행동을 실행한 결과를 확인한다
에이전트 실행기 AgentExecutor 에이전트와 도구를 감싸 사이클을 실제로 반복 실행하는 컴포넌트
에이전트 스크래치패드 agent_scratchpad 지금까지의 생각·행동·관찰 기록이 누적되는 프롬프트 변수
랭체인 허브 LangChain Hub 검증된 프롬프트를 이름으로 내려받을 수 있는 저장소. hub.pull("이름")으로 불러온다
파싱 오류 처리 handle_parsing_errors 모델이 정해진 출력 형식을 어겼을 때 에이전트가 자동으로 복구하도록 하는 옵션

(원문 영문명이 확인되지 않는 용어는 없어 모두 병기했다)

1. 생각의 사슬(Chain of Thought, CoT)

ReAct 방법론은 프린스턴 대학교와 구글 연구원들이 공동으로 개발했다. ReAct는 Reason(추론)과 Act(행동)를 결합한 용어이며, 대규모 언어 모델이 추론과 행동을 유기적으로 함께 사용하며 문제를 해결하는 방식을 뜻한다. 핵심은 모델이 이전에 경험하지 못한 상황이나 정보가 부족한 상황에서도 스스로 적절한 의사결정을 통해 문제를 해결하도록 돕는 것이다. 인간의 자연스러운 문제 해결 과정을 모델링해 만들어졌으며, '생각(Thought) → 행동(Action) → 행동 입력(Action Input) → 관찰(Observation)' 사이클을 목표를 달성할 때까지 반복하는 구조다.

한 사이클의 각 단계는 다음 의미를 지닌다. - 생각(Thought) — 현재 상황을 분석하고 다음 행동을 추론하는 단계. - 행동(Action) — 필요한 구체적 행동을 선택하는 단계. - 행동 입력(Action Input) — 선택한 행동에 필요한 구체적인 값을 정하는 단계. - 관찰(Observation) — 행동 결과를 확인하는 단계이며, 이를 바탕으로 다음 생각으로 이어진다.

원문은 이 사이클을 일상 예시로 설명한다. 파스타를 요리하는 상황을 가정해 보자.

첫 번째 생각(Thought)은 "파스타를 삶아야 하는데 물의 양을 정확히 모르겠다. 요리책을 봐야겠다"이고, 행동(Action)은 요리책 펼치기, 행동 입력(Action Input)은 목차에서 '파스타' 항목을 찾아 해당 페이지로 이동하는 것이다. 관찰(Observation)로 "100g당 물 1L가 필요하다"는 레시피 정보를 얻는다. 두 번째 생각은 이 관찰 결과와 요리할 양(300g)을 결합해 "물이 3L 필요하겠다. 계량컵을 찾아야겠다"로 이어지고, 행동(주방 서랍 찾아보기)과 행동 입력(아래 서랍 열어보기)을 거쳐 계량컵을 찾았다는 관찰로 마무리된다. 이처럼 우리는 일상에서 생각으로 상황을 파악하고, 행동을 결정해 실행하며, 그 결과를 관찰하고, 다시 다음 생각을 하는 과정을 반복적으로 거친다. ReAct는 이 자연스러운 문제 해결 방식을 대규모 언어 모델로 구현한 것이다.

생각의 사슬은 대규모 언어 모델이 답을 최종적으로 결정하기 전에, 답을 찾아가는 과정을 글로 먼저 써 내려가도록 하면 모델이 정답에 더 정확하게 도달할 수 있다는 아이디어다. "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models"(Wei et al., arXiv:2201.11903, 2022) 논문이 이 방법을 대규모 언어 모델의 답변 정확도를 높이는 방법으로 제시했고, ReAct 방법론도 이 개념을 차용해 모델이 다음 행동을 결정하기 전에 반드시 '생각'을 거치도록 만든다.

원문은 일반 프롬프트와 생각의 사슬 프롬프트의 차이를 예시로 보여준다. 일반 프롬프트에서는 대규모 언어 모델에게 '홍길동의 테니스 공 문제' 예시를 주고, 그 답을 '정답: 11개'처럼 풀이 과정 없이 단답형으로 알려줬다. 대규모 언어 모델은 예시를 흉내 내려는 특성이 있어, 실제로 답해야 하는 '카페에 있는 사과' 문제에도 단답형으로 답했다. 그렇게 나온 '정답: 27개'는 오답이었다. 이번에는 같은 '테니스 공 문제' 예시에서 답을 적기 전에 풀이 과정부터 적도록 유도했다. 그러자 모델은 '카페에 있는 사과' 문제에도 풀이 과정을 먼저 작성한 뒤 답변했고, 이렇게 작성한 답변은 정답이었다. 항상 보장되는 것은 아니지만, 풀이 과정(또는 생각)을 거친 후 답변하도록 하면 성능이 오르는 경우가 많다 — 대규모 언어 모델에서 심심찮게 발견되는 현상이다.

정리하면, 생각의 사슬은 ReAct 방법론에만 쓰이는 아이디어가 아니라 대규모 언어 모델이 답을 결정해야 하는 다양한 문제에 두루 쓸 수 있다. 그리고 이 생각의 사슬 프롬프트를 이용해 대규모 언어 모델이 도구의 선택이나 입력값을 적절히 사용하도록 유도하는 것이 이번 장에서 다루는 ReAct 방법론이다.

2. 에이전트 RAG

에이전트 RAG는 RAG에 대규모 언어 모델의 의사결정 능력을 결합한 시스템이다. 여기서 '에이전트'란 특정 목표를 달성하기 위해 현재 상황을 인식하고, 목표 해결을 위해 방법을 추론하며, 다음에 할 행동을 계획하고 실행할 수 있는 AI 시스템을 의미한다. 앞서 설명한 '생각 → 행동 → 행동 입력 → 관찰' 사이클을 목표를 달성할 때까지 반복하는 ReAct 방법론은 대표적인 에이전트 구현 방식 중 하나다.

이 절에서는 서로 다른 두 개의 PDF 파일로부터 두 개의 검색기를 각각 만들고, 이를 ReAct 에이전트와 연결해 복잡한 질문을 처리할 수 있는 에이전트 RAG를 구현한다.

2.1 라이브러리와 데이터 준비

실습 코드는 책의 깃허브 저장소 7장 폴더의 ch07_REACT_AGENT.ipynb 파일이다. 구글 코랩에서 새 노트북을 만들어 코드를 작성한다.

원문 표기와의 차이 — 원문 인쇄본은 이 절의 실습 라이브러리로 !pip install torch==2.4.0 transformers==4.45.1 datasets==3.0.1 accelerate==0.34.2 trl==0.11.1 peft==0.13.0을 제시한다. 그러나 이 목록은 8장·9장의 LLM·임베딩 파인튜닝(로라 설정·모델 학습)에 쓰이는 패키지이고, 이 장의 코드는 아래 임포트 목록만 실제로 사용한다. 학습자료는 이 장의 코드가 실제로 쓰는 패키지 기준으로 고쳐 싣는다.

pip install langchain langchain-openai langchain-community langchainhub chromadb pymupdf python-dotenv

이제 실습에 쓸 도구들을 임포트한다.

import os
import requests
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.vectorstores import Chroma
from langchain.tools.retriever import create_retriever_tool
from langchain import hub
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.prompts import PromptTemplate

각 도구의 쓰임새는 다음과 같다.

  • os — API 키처럼 중요한 정보를 다룰 때 쓰는 기본 라이브러리. 여기서는 오픈AI API 키 값을 읽는 데 쓴다.
  • requests — 인터넷에서 정보나 파일을 요청할 때 쓴다. 여기서는 실습에 쓸 데이터를 다운로드하는 데 쓴다.
  • PyMuPDFLoader — PDF 문서를 읽어들이고 텍스트를 추출하는 도구.
  • RecursiveCharacterTextSplitter — 긴 문서를 의미 있는 단위로 분할하는 도구로, 문장과 단락의 문맥을 보존하며 텍스트를 청크 단위로 나눈다.
  • OpenAIEmbeddings — 오픈AI의 임베딩 모델로 텍스트를 벡터로 변환한다.
  • Chroma — 벡터화된 텍스트를 저장하고 검색하는 벡터 데이터베이스.
  • create_retriever_tool — 벡터 검색을 ReAct 에이전트의 도구로 변환한다.
  • hub — 랭체인의 프롬프트 템플릿 저장소(LangChain Hub)에 접근한다.
  • ChatOpenAI — 오픈AI의 챗GPT 계열 모델을 활용하기 위한 인터페이스.
  • AgentExecutor·create_react_agent — ReAct 에이전트를 생성하고 실행하는 핵심 컴포넌트.
  • PromptTemplate — 에이전트의 프롬프트를 템플릿화해 관리한다.

앞선 실습과 마찬가지로 답변 생성에 챗GPT API를 쓰므로, 오픈AI API 키를 .env 파일에서 불러온다.

# .env 파일에서 환경 변수 로드
load_dotenv("/content/.env")
# 환경 변수에서 API 키 가져오기
api_key = os.getenv("OPENAI_API_KEY")

이어서 실습에 쓸 데이터를 인터넷에서 내려받는다.

urls = [
    "https://raw.githubusercontent.com/langchain-kr/langchain-tutorial/main/Ch07.%20Agent/ict_japan_2024.pdf",
    "https://raw.githubusercontent.com/langchain-kr/langchain-tutorial/main/Ch07.%20Agent/ict_usa_2024.pdf",
]
# 각 파일 다운로드
for url in urls:
    filename = url.split("/")[-1]  # URL에서 파일명 추출
    response = requests.get(url)
    with open(filename, "wb") as f:
        f.write(response.content)
    print(f"{filename} 다운로드 완료")
ict_japan_2024.pdf 다운로드 완료
ict_usa_2024.pdf 다운로드 완료

이 코드는 2024년 발행된 일본과 미국의 ICT 정책 보고서 PDF를 로컬 환경으로 가져온다. urls 리스트는 깃허브 raw 콘텐츠 URL을 담고 있고, 반복문은 각 URL의 파일을 차례로 내려받는다. split() 메서드로 URL의 마지막 부분에서 파일명을 추출하고, requests로 원격 파일 내용을 받아와 바이너리 쓰기 모드로 저장한다. 저장이 끝날 때마다 완료 메시지가 출력된다. 두 나라로 파일을 나눈 이유는 뒤에서 국가별로 독립된 검색기(japan_ict·usa_ict)를 각각 만들어, 에이전트가 질문의 성격에 따라 알맞은 도구를 고르게 하기 위해서다.

2.2 에이전트 도구 만들기

이제 두 PDF 파일을 임베딩하고 벡터 데이터베이스에 적재한 다음, ReAct 에이전트가 상황에 따라 골라 쓸 수 있는 도구로 정의한다.

문서들을 임베딩하기 위해 오픈AI 임베딩 모델 객체를 선언한다.

# 임베딩 설정
embd = OpenAIEmbeddings()

PDF 문서를 벡터 데이터베이스로 변환하고, 사용자 질의와 유사한 문서를 반환하는 검색기 객체(retriever)를 만드는 함수 create_pdf_retriever를 구현한다.

def create_pdf_retriever(
    pdf_path: str,                        # PDF 파일 경로
    persist_directory: str,               # 벡터 스토어 저장 경로
    embedding_model: OpenAIEmbeddings,     # 오픈AI 임베딩 모델
    chunk_size: int = 512,                # 청크 크기, 기본값 512
    chunk_overlap: int = 0,               # 청크 오버랩 크기, 기본값 0
):
    # PDF 파일 로드
    loader = PyMuPDFLoader(pdf_path)
    data = loader.load()
    # 청킹
    text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
    )
    doc_splits = text_splitter.split_documents(data)
    # 벡터 스토어로 적재
    vectorstore = Chroma.from_documents(
        documents=doc_splits,
        embedding=embedding_model,
        persist_directory=persist_directory,
    )
    return vectorstore.as_retriever()

create_pdf_retriever 함수는 PDF 파일 경로·벡터 저장소 경로·오픈AI 임베딩 모델을 필수로 받고, 청크 크기와 오버랩 크기는 선택적 매개변수로 기본값 512와 0을 갖는다. 내부 처리는 세 단계다. 먼저 PyMuPDF로 PDF를 읽어들인다. 다음으로 RecursiveCharacterTextSplitter로 긴 문서를 여러 개의 청크로 분할한다. 마지막으로 분할된 문서들을 오픈AI 임베딩 모델로 벡터화해 Chroma 벡터 데이터베이스에 저장하고, 그 검색 인터페이스(retriever)를 반환한다. 이 retriever는 자연어 질의에 대해 관련성 높은 PDF 내용을 검색할 수 있는 검색기다.

이 함수로 일본 ICT 정책을 검색하는 검색기와 미국 ICT 정책을 검색하는 검색기를 각각 만든다.

# 일본 ICT 정책 데이터베이스 생성
retriever_japan = create_pdf_retriever(
    pdf_path="ict_japan_2024.pdf",
    persist_directory="db_ict_policy_japan_2024",
    embedding_model=embd,
)
# 미국 ICT 정책 데이터베이스 생성
retriever_usa = create_pdf_retriever(
    pdf_path="ict_usa_2024.pdf",
    persist_directory="db_ict_policy_usa_2024",
    embedding_model=embd,
)

persist_directorydb_ict_policy_japan_2024db_ict_policy_usa_2024라는 서로 다른 경로를 지정함으로써, 두 문서의 벡터 데이터베이스가 서로 영향을 주지 않고 독립적으로 저장·관리된다. 같은 경로를 쓰면 두 번째 문서를 처리할 때 첫 번째 문서의 데이터가 덮어써지거나 섞일 수 있으므로, 이렇게 경로를 분리하는 것이 중요하다.

두 검색기를 만들었으니, 이제 create_retriever_tool 함수로 ReAct 에이전트가 쓸 수 있는 검색 도구로 변환한다.

jp_engine = create_retriever_tool(
    retriever=retriever_japan,
    name="japan_ict",
    description="일본의 ICT 시장 동향 정보를 제공합니다. 일본 ICT와 관련된 질문은 해당 도구를 사용하세요.",
)
usa_engine = create_retriever_tool(
    retriever=retriever_usa,
    name="usa_ict",
    description="미국의 ICT 시장 동향 정보를 제공합니다. 미국 ICT와 관련된 질문은 해당 도구를 사용하세요.",
)
tools = [jp_engine, usa_engine]

create_retriever_tool 함수는 retriever 객체를 ReAct 에이전트가 활용할 수 있는 도구 형태로 바꿔준다. 앞서 만든 retriever_japanretriever_usa를 바탕으로 도구 이름(name)과 설명(description)을 붙여 만든다. description에는 각 검색기의 상세한 용도를 작성해야 한다. 이후 ReAct 에이전트가 동작할 때, 에이전트는 여기 적힌 설명을 보고 사용자 질문에 따라 도구를 선택한다. 설명이 자세히 적혀 있지 않으면 에이전트는 상황에 맞는 도구를 제대로 선택할 수 없다. 만들어진 jp_engineusa_enginetools 리스트에 담기고, 이 리스트가 나중에 코드에서 ReAct 에이전트에게 전달된다.

2.3 에이전트 프롬프트 설정

이제 ReAct 에이전트를 동작시키는 프롬프트를 작성한다. 랭체인에는 ReAct 에이전트를 동작시킬 때 기본값으로 제공하는 프롬프트가 있지만, 너무 단순해서 에이전트가 제대로 동작하도록 유도하려면 좀 더 자세하게 작성하는 것이 좋다. 먼저 랭체인이 기본 제공하는 프롬프트를 확인해 보자.

prompt_react = hub.pull("hwchase17/react")
print(prompt_react.template)
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {input}
Thought:{agent_scratchpad}

이 기본 프롬프트를 한글로 옮기면 다음과 같다.

주어진 질문들에 대해 최선을 다해 답변하세요. 다음과 같은 도구들을 사용할 수 있습니다:
{tools}
다음 형식을 사용하세요:
Question: 답변해야 할 입력 질문
Thought: 무엇을 해야 할지 항상 고민해야 합니다.
Action: 수행할 행동(반드시 [{tool_names}] 중 하나여야 함)
Action Input: 행동에 필요한 입력값
Observation: 행동의 결과
....(이 Thought/Action/Action Input/Observation 과정은 N번 반복될 수 있습니다)
Thought: 이제 최종 답을 알았습니다.
Final Answer: 원래 입력 질문에 대한 최종 답변
시작!
Question: {input}
Thought: {agent_scratchpad}

프롬프트 안에서 중괄호 { }로 감싼 부분은 변수다. 실제 실행 시 각 변수에 값이 채워진다.

  • {tools} — 에이전트가 쓸 수 있는 도구들의 설명이 포함된 목록. 이번 실습에서는 create_retriever_tool()로 정의한 도구들의 이름(name)과 설명(description)이 여기 들어간다. 즉 japan_ictusa_ict 두 도구에 대한 설명이 포함된다.
  • {tool_names} — 에이전트가 선택할 수 있는 도구의 이름들. {tools}와 달리 설명 없이 이름만 리스트 형태로 들어간다. 이 실습에서는 [japan_ict, usa_ict]가 들어간다.
  • {input} — 사용자가 현재 물어본 질문. 에이전트는 이 질문을 해결하기 위해 주어진 도구들을 활용한다.
  • {agent_scratchpad} — 에이전트의 모든 사이클(Thought/Action/Observation의 기록)이 누적되는 자리. 이를 통해 에이전트는 이전 사이클을 참고해 다음 행동을 결정한다.

프롬프트의 구성을 보면, 먼저 에이전트가 쓸 수 있는 도구들을 설명하고 'Thought/Action/Action Input/Observation' 사이클을 N번 반복할 수 있다고 안내한다. 주목할 점은 Thought 단계를 각 Action 전후에 두도록 지시한다는 점이다. 이는 에이전트가 행동을 취하기 전에 충분히 고민하고, 행동 결과를 관찰한 뒤에도 다시 한번 생각하며 사이클을 돌도록 유도한다. 또한 Observation이 충분히 취합되면 Final Answer 전에 "이제 최종 답을 알았습니다"라는 명시적인 마지막 생각 단계를 작성하게 해, 에이전트가 자신의 결론에 확신을 가질 때 답변하도록 설계됐다.

이번 실습에서는 기본 프롬프트 대신 한글로 바꾸고 설명을 더 추가한 프롬프트를 쓴다.

template = '''다음 질문에 최선을 다해 답변하세요. 당신은 다음 도구들에 접근할 수 있습니다:
{tools}
다음 형식을 사용하세요:
Question: 답변해야 하는 입력 질문
Thought: 무엇을 할지 항상 생각하세요.
Action: 취해야 할 행동, [{tool_names}] 중 하나여야 합니다. 리스트에 있는 도구 중 1개를 택하십시오.
Action Input: 행동에 대한 입력값
Observation: 행동의 결과
... (이 Thought/Action/Action Input/Observation의 과정이 N번 반복될 수 있습니다)
Thought: 이제 최종 답변을 알겠습니다.
Final Answer: 원래 입력된 질문에 대한 최종 답변

## 추가적인 주의사항
반드시 [Thought -> Action -> Action Input format] 이 사이클의 순서를 준수하십시오. 항상 Action 전에는 Thought가 먼저 나와야 합니다.
- 최종 답변에는 최대한 많은 내용을 포함하십시오. 한 번의 검색으로 해결되지 않을 것 같다면 문제를 분할하여 푸는 것이 중요합니다.
- 정보가 취합되었다면 불필요하게 사이클을 반복하지 마십시오.
- 묻지 않은 정보를 찾으려고 도구를 사용하지 마십시오.

시작하세요!
Question: {input}
Thought: {agent_scratchpad}'''
prompt = PromptTemplate.from_template(template)

기본으로 제공되는 프롬프트와 달리 '## 추가적인 주의사항'이라는 내용이 추가됐는데, 더 나은 성능을 얻기 위해 저자가 임의로 추가한 내용이다. 이처럼 ReAct 에이전트를 쓸 때는 직접 작성한 커스텀 프롬프트로 더 자세하게 설명하면 좋은 성능을 얻을 수 있다.

2.4 에이전트 객체 생성

이제 작성한 프롬프트를 대규모 언어 모델에 설정해 에이전트 객체를 만든다.

# gpt-4o로부터 llm 객체를 선언
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# prompt_react를 사용하면 영어 프롬프트, prompt를 사용하면 한글 프롬프트
react_agent = create_react_agent(llm, tools=tools, prompt=prompt)
react_agent_executor = AgentExecutor(
    agent=react_agent, tools=tools, verbose=True, handle_parsing_errors=True
)

참고 — 이 코드는 원문이 집필된 시점(2025년)에 쓸 수 있었던 모델(gpt-4o)을 그대로 옮긴 것이다. 실습 시점에 쓸 수 있는 모델로 바꿔 써도 ReAct 사이클의 구조 자체는 달라지지 않는다.

랭체인의 ChatOpenAI()로 llm 객체를 생성한다. gpt-4o 모델을 쓰고, 답변의 창의성을 제어하는 temperature 값을 0으로 설정해 초기화한다. 에이전트를 만들 때는 예측하지 못한 창의적인 답변보다 지시사항에 명확히 따르는 동작을 기대하므로 temperature를 0으로 낮춰 답변의 다양성을 줄인다.

앞서 만든 검색 도구들(tools)과 llm 객체를 결합해 react_agent를 만든다. 이때 앞서 작성한 커스텀 프롬프트인 prompt 매개변수를 전달한다. 마지막으로 react_agenttoolsAgentExecutor로 감싼다. verbose=True로 설정하면 모델이 최종 답변을 얻기까지 몇 번에 걸쳐 Thought/Action/Action Input/Observation 사이클을 도는지 사용자도 볼 수 있다. handle_parsing_errors=True로 설정하면 파싱 오류가 발생하더라도 자동으로 처리한다. 여기서 파싱 오류란 Thought/Action/Action Input/Observation을 작성하는 과정에서 간혹 대규모 언어 모델이 형식을 잘못 작성하는 경우를 뜻한다. 이렇게 만든 react_agent_executor가 최종 에이전트 객체이며, 사용자 질문에 대해 적절한 도구를 선택하고 응답을 생성한다.

2.5 에이전트 RAG 실습

이제 에이전트에게 질문을 입력하고 답을 얻어 보자. react_agent_executor.invoke({"input": "사용자의 질문"}) 형식으로 질문을 입력한다. 먼저 "한국과 미국의 ICT 기관 협력 사례"를 물어본다.

result = react_agent_executor.invoke({"input": "한국과 미국의 ICT 기관 협력 사례"})

이 질문은 미국의 ICT 시장과 관련이 있으므로, 미국의 ICT 시장 동향 정보를 통해 답을 찾을 수 있을 것이다. 실행 결과를 정리하면 다음과 같다(랭체인은 'Observation:' 문자열 자체를 출력하지 않으므로, Action Input과 다음 Thought 사이에 나오는 내용이 곧 검색 결과다).

Thought(첫 번째 생각): 이 질문은 미국의 ICT 시장과 관련이 있으므로, 미국의 ICT 시장 동향 정보를 통해
한국과 미국의 ICT 기관 협력 사례를 찾을 수 있을 것입니다.
Action(도구 선택): usa_ict
Action Input(도구 입력): 한국과 미국의 ICT 협력 사례
Observation(검색 결과): 한국-미국 ICT 기업 진출 사례 ... (중간 생략) ...
Thought(두 번째 생각): 한국과 미국의 ICT 기관 협력 사례에 대한 정보를 찾았습니다. 이제 이를 바탕으로
최종 답변을 제공하겠습니다.
Final Answer(최종 답변): 한국과 미국의 ICT 기관 협력 사례로는 여러 가지가 있습니다. 2012년 3월
한-미 FTA가 발효되었고, 2019년 1월에는 한-미 FTA 개정 의정서가 발효되었습니다. 2022년에는
한-미 FTA 10주년을 기념하여 정부 및 국회 대표단이 미국을 방문하였습니다. 또한 2023년 1월에는
경북도가 산타클라라 한인상공회의소와 '실리콘밸리 스타트업 아카데미 경북 MOU'를 개최하였고,
광주시는 AI 스타트업의 미국 진출을 지원하였습니다.

에이전트는 첫 번째 생각에서 이 질문이 미국 ICT 시장과 관련이 있다고 판단해 usa_ict 도구를 골랐다. 도구 실행 결과(관찰)를 확인한 뒤 두 번째 생각에서 질문을 해결하기에 충분한 정보를 얻었다고 판단하고 최종 답변을 작성했다. 최종 답변만 따로 꺼내려면 다음처럼 쓴다.

print('최종 답변:', result['output'])
최종 답변: 한국과 미국의 ICT 기관 협력 사례로는 한-미 FTA 체결과 개정, 경북도와 산타클라라
한인상공회의소의 MOU 체결, 광주시의 AI 스타트업 미국 진출 지원 등이 있습니다. 또한 과기정통부와
미국 기관 간의 ICT 협력도 주목받고 있습니다.

같은 질문을 다시 실행하면 최종 답변이 여기 실린 것과 다소 달라질 수 있다 — 실습이 실행되는 시점의 검색 결과와 모델 응답에 따라 달라지기 때문이다.

이번에는 두 도구를 모두 호출해야 답할 수 있는 질문 "미국과 일본의 ICT 주요 정책의 공통점과 차이점을 설명해줘"를 입력해 보자.

result = react_agent_executor.invoke(
    {"input": "미국과 일본의 ICT 주요 정책의 공통점과 차이점을 설명해줘."}
)
Thought(첫 번째 생각): 미국과 일본의 ICT 주요 정책의 공통점과 차이점을 설명하려면 두 나라의 ICT
정책 정보를 각각 수집해야 합니다.
Action(도구 선택): usa_ict
Action Input(도구 입력): 미국의 ICT 주요 정책에 대한 정보
Observation(검색 결과): ICT 국가산업현황 ... (중간 생략) ...
Thought(두 번째 생각): 미국의 ICT 주요 정책에 대한 정보를 얻었습니다. 이제 일본의 ICT 주요 정책에
대한 정보를 수집해야 합니다.
Action(도구 선택): japan_ict
Action Input(도구 입력): 일본의 ICT 주요 정책에 대한 정보
Observation(검색 결과): ICT 국가산업현황 ... (중간 생략) ...
Thought(세 번째 생각): 미국과 일본의 ICT 주요 정책에 대한 정보를 수집했습니다. 이제 공통점과
차이점을 분석할 수 있습니다.
Final Answer(최종 답변): 공통점 — 두 나라 모두 기술 혁신으로 산업 경쟁력을 강화하려 하며(미국은
CHIPS 법안, 일본은 산업경쟁력 강화법 개정), 국제 협력으로 기술 발전을 도모합니다(미국은 싱가포르와
전략적 기술 파트너십, 일본은 미국과 양자컴퓨팅 협력). 차이점 — 미국은 반도체 같은 특정 기술 분야에
집중하는 반면 일본은 Web3 같은 새 기술 분야에 초점을 맞추고, 일본은 정부 행정에 생성형 AI를
도입하는 등 공공 부문을, 미국은 의료 AI 연구 같은 민간 부문 활용을 더 강조합니다.

실행 과정을 보면 첫 번째 생각에서 두 도구를 모두 써야겠다는 계획을 세우고, usa_ict를 먼저 호출해 미국 정보를 얻은 뒤 두 번째 생각에서 japan_ict를 호출해 일본 정보를 얻는다. 두 번의 관찰 결과를 바탕으로 세 번째 생각에서 공통점과 차이점을 종합해 최종 답변을 작성한다.

이번에는 더 복잡한 질문을 입력해 보자. usa_ict를 세 번, japan_ict를 최소 한 번 호출해야 답할 수 있는 질문이다.

result = react_agent_executor.invoke({
    "input": "미국의 ICT 관련 정부 기구, 주요 법령, 국내 기업 진출 사례 각각 따로 검색해. "
             "그렇게 해서 정보 좀 모아봐. 그리고 나서 일본의 AI 정책도 알려줘."
})
Thought(첫 번째 생각): 각 항목을 별도로 검색하겠습니다. 먼저 미국의 ICT 관련 정부 기구를
알아보겠습니다.
Action: usa_ict / Action Input: 미국의 ICT 관련 정부 기구
Observation: ... (중간 생략) ...
Thought(두 번째 생각): 이제 미국의 ICT 관련 주요 법령을 수집해야 합니다.
Action: usa_ict / Action Input: 미국의 ICT 주요 법령에 대한 정보
Observation: ... (중간 생략) ...
Thought(세 번째 생각): 미국의 ICT 관련 국내 기업 진출 사례를 알아보겠습니다.
Action: usa_ict / Action Input: 미국의 ICT 관련 국내 기업 진출 사례
Observation: ... (중간 생략) ...
Thought(네 번째 생각): 이제 일본의 AI 정책을 알아보겠습니다.
Action: japan_ict / Action Input: 일본의 AI 정책
Observation: ... (중간 생략) ...
Thought(다섯 번째 생각): 이제 모든 정보를 수집했습니다. 최종 답변을 정리하겠습니다.
Final Answer(최종 답변):
1. 미국의 ICT 관련 정부 기구 — 연방통신위원회(FCC)는 통신·방송 정책 총괄과 시장 규제를,
국가통신정보관리청(NTIA)은 대통령 자문과 AI 정책 권장 사항 개발을 담당합니다.
2. 미국의 ICT 관련 주요 법령 — 반도체 과학법(CHIPS and Science Act, 2022)은 공급망을
탄력적으로 구축해 국가 안보를 강화하는 것을 목표로 합니다.
3. 미국의 ICT 관련 국내 기업 진출 사례 — 한-미 FTA 발효·개정을 통한 협력 강화, 경북도와
산타클라라 한인상공회의소의 MOU, 광주시의 AI 스타트업 지원 등이 있습니다.
4. 일본의 AI 정책 — 경제산업성은 생성 AI 활용 인재 육성 지침을 마련했고, 개인정보보호위원회는
생성형 AI 서비스 사용에 관한 지침을 발표했습니다.

이 실행 과정은 하나의 복잡한 질문을 usa_ict 세 번, japan_ict 한 번으로 이어지는 네 개의 단순한 하위 질의로 쪼개 순차적으로 처리한 것을 보여준다. 각 호출마다 관찰 결과를 확인한 뒤 다음 생각으로 넘어가며, 모든 정보가 모이면 "이제 모든 정보를 수집했습니다"라는 생각을 거쳐 체계적인 최종 답변을 작성한다. 이처럼 에이전트는 간단한 질문부터 다수의 검색 도구가 필요한 복잡한 질문까지, 일반적인 RAG로는 해결하기 어려운 문제도 순차적으로 차근차근 해결하는 능력을 갖고 있다.

핵심 개념 정리

개념 한 줄 설명
ReAct 추론(Reason)과 행동(Act)을 결합해 '생각→행동→행동 입력→관찰' 사이클을 반복하는 에이전트 방법론
생각의 사슬(CoT) 답을 정하기 전에 풀이 과정을 먼저 쓰게 해 정답률을 높이는 프롬프트 기법
에이전트 RAG RAG에 대규모 언어 모델의 의사결정 능력을 결합해, 여러 검색 도구 중 상황에 맞는 것을 스스로 고르게 하는 시스템
create_pdf_retriever PDF를 로드→청킹→임베딩→Chroma 저장까지 거쳐 검색기를 반환하는 사용자 정의 함수
create_retriever_tool retriever를 이름(name)·설명(description)을 붙여 에이전트가 고를 수 있는 도구로 변환하는 함수
description의 역할 에이전트가 도구를 선택하는 유일한 판단 근거 — 부실하면 엉뚱한 도구를 고르거나 놓친다
프롬프트 변수 4종 {tools}(도구 설명 전체)·{tool_names}(도구 이름만)·{input}(사용자 질문)·{agent_scratchpad}(누적 기록)
AgentExecutor 에이전트와 도구를 감싸 사이클을 실제로 반복 실행하는 최종 실행기
복합 질문 분해 에이전트는 복잡한 질문을 여러 단순 하위 질의로 쪼개 도구를 순차 호출한 뒤 결과를 종합한다

실무 체크리스트

  • [ ] 도구의 description을 "이 도구가 언제 필요한가"까지 구체적으로 적었는가?
  • [ ] 여러 데이터 소스를 쓸 때 persist_directory를 소스마다 다르게 지정했는가?
  • [ ] 에이전트가 선택할 도구 후보가 서로 명확히 구분되는 주제를 다루는가(겹치면 잘못된 도구를 고를 수 있다)?
  • [ ] 프롬프트에 Thought를 Action 전후에 두도록 명시했는가?
  • [ ] 커스텀 프롬프트에 "불필요한 사이클 반복 금지"·"묻지 않은 정보 검색 금지" 같은 제약을 넣었는가?
  • [ ] AgentExecutorhandle_parsing_errors=True를 설정해 형식 오류에 대비했는가?
  • [ ] 에이전트를 만들 때 temperature를 낮춰 지시 이행 정확도를 우선했는가?
  • [ ] 복합 질문을 테스트해 에이전트가 여러 도구를 순서대로 호출하는지 확인했는가?
  • [ ] 결과 재현성이 필요하지 않은 실습(검색·생성 결과가 매번 다를 수 있음)임을 학습자에게 안내했는가?

연습문제

  1. 개념. ReAct 방법론에서 '생각(Thought)'이 각 행동(Action) 전후에 반드시 오도록 설계된 이유를 설명하라.
  2. 비교. {tools}{tool_names} 프롬프트 변수가 담는 값이 어떻게 다른지, 그리고 각각이 에이전트의 어떤 판단에 쓰이는지 설명하라.
  3. 실무 시나리오. 세 번째 나라(예: 독일)의 ICT 정책 PDF를 추가해 검색기를 하나 더 만들려고 한다. create_pdf_retrievercreate_retriever_tool을 각각 어떻게 호출해야 하는지, 그리고 tools 리스트는 어떻게 바뀌는지 설명하라.
  4. 실무 시나리오. 에이전트가 관련 없는 질문에도 계속 도구를 호출하며 사이클을 반복해 응답이 느려지는 문제가 보고됐다. 프롬프트의 어느 부분을 점검하고 어떻게 고쳐야 하는지 설명하라.

최신 동향 (2026-09 기준)

최신 동향 (검증 2026-09-12, Tavily+Brave — SearXNG는 무관 결과만 반환해 미반영, 실질 N=2) — 책의 ReAct·CoT 개념과 실습 흐름은 그대로 유효하다. 다만 이 장이 쓰는 랭체인 API 계층은 이 책 이후 크게 바뀌었다.

  • 이 장이 쓰는 langchain.agentsAgentExecutor·create_react_agent(그 이전의 initialize_agent 포함)는 랭체인 0.2부터 레거시로 분류되어 치명적 결함 수정만 받는 상태다. 랭체인은 이 방식 대신 LangGraph 기반 에이전트로 옮기도록 권장한다.
  • 한 단계 더 나아가, 그 대안으로 쓰이던 langgraph.prebuiltcreate_react_agent조차 이후 LangChain v1 마이그레이션 가이드에서 폐기 예정으로 분류되었고, langchain.agentscreate_agent로 옮기도록 안내한다(공식 레퍼런스: langgraph.prebuilt.create_react_agent).
  • 즉 이 장의 코드를 그대로 실행하면 여전히 동작하지만(레거시 유지보수 지원), 새 프로젝트를 시작한다면 create_agent 계열의 최신 가이드를 함께 확인하는 편이 좋다. 이 장에서 배우는 '도구 설명으로 판단해 스스로 도구를 고른다'는 ReAct의 핵심 개념 자체는 API가 바뀌어도 동일하게 적용된다.

부록 A. 핵심 비교표

구분 일반 프롬프트 생각의 사슬(CoT) 프롬프트
예시로 보여주는 답변 형태 풀이 없이 "정답: N개"만 풀이 과정을 먼저 쓴 뒤 "정답: N개"
모델이 실제 문제에 답하는 방식 예시를 흉내 내 단답형으로 답 예시를 흉내 내 풀이 과정을 먼저 쓴 뒤 답
책 예시의 결과 오답('정답: 27개') 정답
구분 {tools} {tool_names}
담는 내용 각 도구의 이름 + 설명 전체 도구 이름만 나열한 리스트
이 장의 예 japan_ict·usa_ict의 설명 전문 [japan_ict, usa_ict]
에이전트가 쓰는 용도 어떤 도구가 무엇을 하는지 판단 Action에 쓸 수 있는 값의 범위 제한
구분 react_agent react_agent_executor
정체 프롬프트·도구 정보로 다음 행동을 결정하는 에이전트 로직 에이전트와 도구를 감싸 사이클을 실제로 반복 실행하는 실행기
실제로 .invoke()를 호출하는 대상 아니오
구분 랭체인 기본 프롬프트(hwchase17/react) 이 장의 커스텀 프롬프트
언어 영어 한글
추가 지시 없음 '## 추가적인 주의사항'(사이클 순서 준수·불필요한 반복 금지 등)

부록 B. 추천 참고 자료

외부 자료 (Tier 1 공식·논문, 생존 확인 2026-09-12)

본 책 연계 챕터

챕터 이 장이 다루지 않은 것
4장 §3~§4 검색 알고리즘(희소·밀집·앙상블)과 문서 후처리(리랭킹) — 이 장의 create_pdf_retriever는 기본 유사도 검색만 쓴다. 도구가 돌려주는 문서 품질을 높이려면 이 절의 방법을 적용할 수 있다
8장 §1 RAFT 논문 — 생각의 사슬을 답변 생성 학습 데이터 자체에 반영하는 방법. 이 장이 프롬프트로 유도한 CoT를 학습 단계로 옮긴 것이다
8장 §4 로컬 LLM Qwen 파인튜닝하기 — 이 장은 gpt-4o API 호출로 에이전트를 만들지만, 자체 호스팅 모델로 같은 역할을 대체하고 싶을 때 참고할 절차다
9장 §1·§3 임베딩 모델 학습의 원리·실전 파인튜닝 — 이 장의 도구(japan_ict·usa_ict)가 쓰는 임베딩 모델 자체를 도메인에 맞게 개선하는 방법

부록 C. 연습문제 풀이

  1. (문제 1 정답) ReAct는 에이전트가 행동을 취하기 전에 현재 상황을 충분히 분석하게 하고, 행동 결과(관찰)를 받은 뒤에도 다시 한번 생각하며 다음 행동을 정하게 하려는 목적으로 설계됐다. 이렇게 하면 에이전트가 근거 없이 도구를 호출하는 대신, 매 단계마다 '왜 이 행동을 하는가'를 먼저 정리하게 되어 상황에 맞는 행동을 더 정확히 고를 수 있다. 또한 정보가 충분히 모이면 "이제 최종 답을 알았습니다"라는 명시적 생각 뒤에만 최종 답변을 내도록 해, 근거 없는 성급한 답변을 줄인다.

  2. (문제 2 정답) {tools}에는 각 도구의 이름과 설명이 함께 들어가 에이전트가 "어떤 도구가 무엇을 하는가"를 판단하는 근거가 된다. {tool_names}에는 설명 없이 도구 이름만 리스트로 들어가며, 에이전트가 Action 단계에서 고를 수 있는 값의 범위를 제한하는 역할을 한다. 즉 {tools}는 도구를 고르는 판단 재료이고, {tool_names}는 그 판단 결과를 표현할 수 있는 형식 제약이다.

  3. (문제 3 정답) 먼저 create_pdf_retriever(pdf_path="ict_germany_2024.pdf", persist_directory="db_ict_policy_germany_2024", embedding_model=embd)처럼 새 PDF 경로와 새 저장 경로로 retriever_germany를 만든다. 그다음 create_retriever_tool(retriever=retriever_germany, name="germany_ict", description="독일의 ICT 시장 동향 정보를 제공합니다. 독일 ICT와 관련된 질문은 해당 도구를 사용하세요.")germany_engine을 만든다. 마지막으로 tools = [jp_engine, usa_engine, germany_engine]처럼 리스트에 새 도구를 추가하면, 이후 create_react_agent 호출 시 세 나라 도구를 모두 인식한다.

  4. (문제 4 정답) 먼저 커스텀 프롬프트의 '## 추가적인 주의사항' 절에 "정보가 취합되었다면 불필요하게 사이클을 반복하지 마십시오"·"묻지 않은 정보를 찾으려고 도구를 사용하지 마십시오"라는 지시가 그대로 남아 있는지, 지워지거나 약해지지는 않았는지 확인한다. 그다음 도구의 description이 서로 겹치는 주제를 다루고 있지는 않은지 점검한다 — 설명이 모호하거나 겹치면 에이전트가 같은 정보를 여러 도구로 반복 확인하려 들 수 있다. 필요하면 "질문과 무관한 배경 정보는 검색하지 말라"는 제약을 프롬프트에 더 명시적으로 추가한다.

난이도
에피소드
질문
카드를 로딩 중...
답변

클릭하거나 Space를 눌러 뒤집기

0 / 0
학습 진도 0%
이동   Space 뒤집기   R 셔플   P 음성 버전 2026-08-05b